/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2018 Live Networks, Inc.  All rights reserved.
// Raw Video RTP Sources (RFC 4175)
// Implementation

#include "include/RawVideoRTPSource.hh"

////////// RawVideoBufferedPacket and RawVideoBufferedPacketFactory //////////

class RawVideoBufferedPacket : public BufferedPacket {
public:
    RawVideoBufferedPacket(RawVideoRTPSource *ourSource);

    virtual ~RawVideoBufferedPacket();

private: // redefined virtual functions
    virtual void getNextEnclosedFrameParameters(unsigned char *&framePtr,
                                                unsigned dataSize,
                                                unsigned &frameSize,
                                                unsigned &frameDurationInMicroseconds);

private:
    RawVideoRTPSource *fOurSource;
};

class RawVideoBufferedPacketFactory : public BufferedPacketFactory {
private: // redefined virtual functions
    virtual BufferedPacket *createNewPacket(MultiFramedRTPSource *ourSource);
};


////////// LineHeader //////////

struct LineHeader {
    u_int16_t length;
    u_int16_t fieldIdAndLineNumber;
    u_int16_t offsetWithinLine;
};


///////// RawVideoRTPSource implementation (RFC 4175) ////////

RawVideoRTPSource *
RawVideoRTPSource::createNew(UsageEnvironment &env, Groupsock *RTPgs,
                             unsigned char rtpPayloadFormat,
                             unsigned rtpTimestampFrequency) {
    return new RawVideoRTPSource(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency);
}

RawVideoRTPSource
::RawVideoRTPSource(UsageEnvironment &env, Groupsock *RTPgs,
                    unsigned char rtpPayloadFormat,
                    unsigned rtpTimestampFrequency)
        : MultiFramedRTPSource(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency,
                               new RawVideoBufferedPacketFactory),
          fNumLines(0), fNextLine(0), fLineHeaders(NULL) {
}

RawVideoRTPSource::~RawVideoRTPSource() {
    delete[] fLineHeaders;
}

u_int16_t RawVideoRTPSource::currentLineNumber() const {
    if (fNextLine == 0 || fLineHeaders == NULL) return 0; // we've called this function too soon!
    return fLineHeaders[fNextLine - 1].fieldIdAndLineNumber & 0x7FFF;
}

u_int8_t RawVideoRTPSource::currentLineFieldId() const {
    if (fNextLine == 0 || fLineHeaders == NULL) return 0; // we've called this function too soon!
    return (fLineHeaders[fNextLine - 1].fieldIdAndLineNumber & 0x8000) >> 15;
}

u_int16_t RawVideoRTPSource::currentOffsetWithinLine() const {
    if (fNextLine == 0 || fLineHeaders == NULL) return 0; // we've called this function too soon!
    return fLineHeaders[fNextLine - 1].offsetWithinLine;
}

Boolean RawVideoRTPSource
::processSpecialHeader(BufferedPacket *packet,
                       unsigned &resultSpecialHeaderSize) {

    unsigned char *headerStart = packet->data();
    unsigned packetSize = packet->dataSize();

    // The first 2 bytes of the header are the "Extended Sequence Number".
    // In the current implementation, we ignore this.
    if (packetSize < 2) return False;
    headerStart += 2;
    unsigned char *lineHeaderStart = headerStart;
    packetSize -= 2;

    // The rest of the header should consist of N*6 bytes (with N >= 1) for each line included.
    // Count how many of these there are:
    unsigned numLines = 0;
    while (1) {
        if (packetSize < 6) return False; // there's not enough room for another line header
        ++numLines;
        Boolean continuationBit = (headerStart[4] & 0x80) >> 7;
        headerStart += 6;
        packetSize -= 6;

        // Check the "C" (continuation) bit of this header to see whether any more line headers follow:
        if (continuationBit == 0) break; // no more line headers follow
    }

    // We now know how many lines are contained in this payload.  Allocate and fill in "fLineHeaders":
    fNumLines = numLines; // ASSERT: >= 1
    fNextLine = 0;
    delete[] fLineHeaders;
    fLineHeaders = new LineHeader[fNumLines];
    unsigned totalLength = 0;
    for (unsigned i = 0; i < fNumLines; ++i) {
        fLineHeaders[i].length = (lineHeaderStart[0] << 8) + lineHeaderStart[1];
        totalLength += fLineHeaders[i].length;
        fLineHeaders[i].fieldIdAndLineNumber = (lineHeaderStart[2] << 8) + lineHeaderStart[3];
        fLineHeaders[i].offsetWithinLine = ((lineHeaderStart[4] & 0x7F) << 8) + lineHeaderStart[5];
        lineHeaderStart += 6;
    }

    // Make sure that we have enough bytes for all of the line lengths promised:
    if (totalLength > packetSize) {
        fNumLines = 0;
        delete[] fLineHeaders;
        fLineHeaders = NULL;
        return False;
    }

    // Everything looks good:
    fCurrentPacketBeginsFrame
            = (fLineHeaders[0].fieldIdAndLineNumber & 0x7FFF) == 0 &&
              fLineHeaders[0].offsetWithinLine == 0;
    fCurrentPacketCompletesFrame = packet->rtpMarkerBit();
    resultSpecialHeaderSize = headerStart - packet->data();
    return True;
}

char const *RawVideoRTPSource::MIMEtype() const {
    return "video/RAW";
}


////////// RawVideoBufferedPacket and RawVideoBufferedPacketFactory implementation //////////

RawVideoBufferedPacket
::RawVideoBufferedPacket(RawVideoRTPSource *ourSource)
        : fOurSource(ourSource) {
}

RawVideoBufferedPacket::~RawVideoBufferedPacket() {
}

void RawVideoBufferedPacket::getNextEnclosedFrameParameters(unsigned char *& /*framePtr*/,
                                                            unsigned dataSize,
                                                            unsigned &frameSize,
                                                            unsigned &frameDurationInMicroseconds) {
    frameDurationInMicroseconds = 0; // because all lines within the same packet are from the same frame

    if (fOurSource->fNextLine >= fOurSource->fNumLines) {
        fOurSource->envir() << "RawVideoBufferedPacket::nextEnclosedFrameParameters("
                            << dataSize << "): data error ("
                            << fOurSource->fNextLine << " >= " << fOurSource->fNumLines << ")!\n";
        frameSize = dataSize;
        return;
    }

    frameSize = fOurSource->fLineHeaders[fOurSource->fNextLine++].length;
}

BufferedPacket *RawVideoBufferedPacketFactory
::createNewPacket(MultiFramedRTPSource *ourSource) {
    return new RawVideoBufferedPacket((RawVideoRTPSource *) ourSource);
}
